package state.component;

import java.util.Vector;

import state.GameConstants;
import state.Graphics;
import state.Image;
import state.Key;
import state.Manager;
import state.ResourceManager;
import state.Sprite;
import state.TimerListener;
import state.bean.Point;
import state.bean.Rect;
import state.font.FontManager;
import state.font.ImageFont;
import state.util.Color;
import state.util.ImageUtil;
import development.Constants;

public class GridMenu extends Component implements TimerListener {
  private Rect gridBorder;
  private int dx;
  private int dy;
  private int borderY;
  private int hozDistance;
  private int verDistance;
  private int numberOfItemPerRow;
  
  private Vector items; // Sprite
  private Vector images; // Image
  private Vector brightImages; // Image
  private Vector titles;
  private String[][] tootltip;
  private int[] eventNumbers;
  private Image roundRedImage;
  
  private int brightLevel = 3;
  private int currentSelectedIndex;
  private long stopTime;
  private long showToolTipTime = 1000;
  private boolean isDisplayTitle;
  private MultiTab multiTab;
  private boolean isNextTabWhenEndRow;
  private boolean isBackTabWhenBeginRow;
  private int initDy;
  private boolean enableScroll;
  
  private Point pressedPoint = new Point(0, 0);
  private long timePressed;
  private int rowMove;
  
  private ImageFont text8;
  
  private EventListener eventListener;
  
  public GridMenu(Manager manager, Image[] images, String[] titles, int numberOfItemPerRow, int dx, int dy, int hozDistance, int verDistance, EventListener eventListener) {
    setManager(manager);
    this.dx = dx;
    this.dy = dy;
    this.initDy = dy;
    this.hozDistance = hozDistance;
    this.verDistance = verDistance;
    this.numberOfItemPerRow = numberOfItemPerRow;
    this.borderY = dy - verDistance / 2;
    enableScroll = true;
    
    // Init image
    this.images = new Vector();
    brightImages = new Vector();
    if (images != null) {
      for (int i = 0; i < images.length; i++) {
        this.images.addElement(images[i]);
        brightImages.addElement(images[i]);
      }
    }
    
    // Create items
    items = new Vector();
    for (int i = 0; i < this.images.size(); i++) {
      int x = dx + hozDistance * (i % numberOfItemPerRow);
      int y = dy + verDistance * (i / numberOfItemPerRow);
      items.addElement(new Sprite(images[i], x, y, GameConstants.CENTER_ANCHOR));
    }
    
    // Create title
    this.titles = new Vector();
    if (titles != null && titles.length > 0) {
      for (int i = 0; i < titles.length; i++) {
        this.titles.addElement(titles[i]);
      }
      isDisplayTitle = true;
    } else {
      isDisplayTitle = false;
    }
    
    this.eventListener = eventListener;
    roundRedImage = ImageUtil.getCoreImage("nen_do.png");
    text8 = FontManager.getFont(FontManager.FONT_SIZE_8);
    hasBorder = false;
    gridBorder = new Rect(dx, dy, hozDistance * numberOfItemPerRow, verDistance * (items.size() / numberOfItemPerRow));
    setTimerListener(this);
  }
  
  public void clearData() {
    if (items != null) {
      items.removeAllElements();
    }
    if (images != null) {
      images.removeAllElements();
    }
    if (brightImages != null) {
      brightImages.removeAllElements();
    }
    if (titles != null) {
      titles.removeAllElements();
    }
    gridBorder.width = 0;
    gridBorder.height = 0;
    isDisplayTitle = false;
    currentSelectedIndex = 0;
    dy = initDy;
  }
  
  public void addItem(Image image, String title) {
    int x = dx + hozDistance * (items.size() % numberOfItemPerRow);
    int y = dy + verDistance * (items.size() / numberOfItemPerRow);
    images.addElement(image);
    brightImages.addElement(image); 
    items.addElement(new Sprite(image, x, y, GameConstants.CENTER_ANCHOR));
    
    if (title != null && titles != null) {
      titles.addElement(title);
    }
    gridBorder.width = hozDistance * numberOfItemPerRow;
    gridBorder.height = verDistance * (items.size() / numberOfItemPerRow);
  }
  
  public void addItem(Sprite sprite, String title) {
    int x = dx + hozDistance * (items.size() % numberOfItemPerRow);
    int y = dy + verDistance * (items.size() / numberOfItemPerRow);
    sprite.setPosition(x, y);
    
    Image image = sprite.getImage();
    if (image == null) {
      image = ResourceManager.createEmptyImage();
    }
    images.addElement(image);
    brightImages.addElement(image); 
    items.addElement(sprite);
    
    if (title != null && titles != null) {
      titles.addElement(title);
    }
    gridBorder.width = hozDistance * numberOfItemPerRow;
    gridBorder.height = verDistance * (items.size() / numberOfItemPerRow);
  }
  
  public void removeItem(int index) {
    if (items.size() > index) {
      items.removeElementAt(index);
    }
    if (images.size() > index) {
      images.removeElementAt(index);
    }
    if (brightImages.size() > index) {
      brightImages.removeElementAt(index);
    }
    if (titles.size() > index) {
      titles.removeElementAt(index);
    }
    
    gridBorder.width = hozDistance * numberOfItemPerRow;
    gridBorder.height = verDistance * (items.size() / numberOfItemPerRow);
    
    if (currentSelectedIndex > items.size() - 1) {
      currentSelectedIndex = items.size() - 1;
      if (currentSelectedIndex < 0) {
        currentSelectedIndex = 0;
      }
    }
  }
  
  public void setEnableScroll(boolean enableScroll) {
    this.enableScroll = enableScroll;
  }
  
  public boolean isEnableScroll() {
    return enableScroll;
  }

  public Image getImageOfItem(int index) {
    if (images.size() < index) {
      return null;
    }
    return (Image) images.elementAt(index);
  }
  
  public Image[] getImageOfAllItem() {
    Image[] results = new Image[images.size()];
    for (int i = 0; i < results.length; i++) {
      results[i] = (Image) images.elementAt(i);
    }
    return results;
  }
  
  public int getNumberOfItem() {
    return items.size();
  }
  
  public String getTitleOfIndex(int index) {
    if (titles == null || titles.size() < index) {
      return "";
    }
    return titles.elementAt(index).toString();
  }
  
  public void setNextTabWhenEndRow(MultiTab multiTab) {
    if (multiTab != null) {
      this.multiTab = multiTab;
      this.isNextTabWhenEndRow = true;
    } else {
      this.isNextTabWhenEndRow = true;
    }
  }
  
  public void setBackTabWhenBeginRow(MultiTab multiTab) {
    if (multiTab != null) {
      this.multiTab = multiTab;
      this.isBackTabWhenBeginRow = true;
    } else {
      this.isBackTabWhenBeginRow = true;
    }
  }
  
  public void setDisplayTitle(boolean isDisplayTitle) {
    this.isDisplayTitle = isDisplayTitle;
  }
  
  public boolean isDisplayTitle() {
    return isDisplayTitle;
  }
  
  public int getCurrentSelectedIndex() {
    return currentSelectedIndex;
  }
  
  public void setCurrentSelectedIndex(int currentSelectedIndex) {
    this.currentSelectedIndex = currentSelectedIndex;
  }
  
  public void setEventListener(EventListener eventListener) {
    this.eventListener = eventListener;
  }
  
  public int getBrightLevel() {
    return brightLevel;
  }

  public void setBrightLevel(int brightLevel) {
    this.brightLevel = brightLevel;
  }
  
  public void setShowToolTipTime(long showToolTipTime) {
    this.showToolTipTime = showToolTipTime;
  }
  
  public long getShowToolTipTime() {
    return showToolTipTime;
  }
  
  public void setEventNumbers(int[] eventNumbers) {
    this.eventNumbers = eventNumbers;
  }
  
  public int[] getEventNumbers() {
    return eventNumbers;
  }
  
  public void setEvent(int eventIndex, int number) {
    if (eventNumbers != null) {
      eventNumbers[eventIndex] = number;
    }
  }
  
  public int getDx() {
    return dx;
  }

  public void setDx(int dx) {
    this.dx = dx;
  }

  public int getDy() {
    return dy;
  }

  public void setDy(int dy) {
    this.dy = dy;
  }

  public int getHozDistance() {
    return hozDistance;
  }

  public void setHozDistance(int hozDistance) {
    this.hozDistance = hozDistance;
  }

  public int getVerDistance() {
    return verDistance;
  }

  public void setVerDistance(int verDistance) {
    this.verDistance = verDistance;
  }

  public int getNumberOfItemPerRow() {
    return numberOfItemPerRow;
  }

  public void setNumberOfItemPerRow(int numberOfItemPerRow) {
    this.numberOfItemPerRow = numberOfItemPerRow;
  }
  
  public void draw(Graphics g) {
    for (int i = 0; i < items.size(); i++) {
      Sprite item = (Sprite) items.elementAt(i);
      int x = dx + hozDistance * (i % numberOfItemPerRow);
      int y = dy + verDistance * (i / numberOfItemPerRow);
      
      if (y < borderY) {
        continue;
      }
      
      item.paint(g);
      if (eventNumbers != null) {
        drawEventNumbers(g, eventNumbers[i], item.getRealX() + item.getWidth(), item.getRealY());
      }
      
      if (isDisplayTitle() && titles != null && titles.size() > i) {
        if (currentSelectedIndex == i) {
          text8.setBold(true);
          text8.drawString(g, String.valueOf(titles.elementAt(i)), focusFgColor, x, y + item.getHeight() / 2 + 10, GameConstants.CENTER_ANCHOR);
          text8.setBold(false);
        } else {
          text8.drawString(g, String.valueOf(titles.elementAt(i)), forceGroundColor, x, y + item.getHeight() / 2 + 10, GameConstants.CENTER_ANCHOR);
        }
      }
      
      if (hasBorder) {
        g.setColor(borderColor);
        g.drawRect(x - hozDistance / 2, y - verDistance / 2, hozDistance, verDistance);
      }
    }
    
    if (tootltip != null && stopTime > showToolTipTime) {
      int x = dx + hozDistance * (currentSelectedIndex % numberOfItemPerRow);
      int y = dy + verDistance * (currentSelectedIndex / numberOfItemPerRow);
      drawToolTip(g, text8, tootltip[currentSelectedIndex], x, y);
    }
  }
  
  public void drawEventNumbers(Graphics g, int number, int x, int y) {
    if (number > 0) {
      g.drawImage(roundRedImage, x - 2, y + 2, Constants.CENTER_ANCHOR);
      text8.drawString(g, fillNumberByLength(number, 2), Color.WHITE_CODE, x, y, Constants.CENTER_ANCHOR);
    }
  }
  
  private String fillNumberByLength(int number, int length) {
    String strNumber = String.valueOf(number);
    int tmp = length - strNumber.length();
    if (tmp > 0) {
      for (int i = 0; i < tmp; i++) {
        strNumber = "0" + strNumber;
      }
    }
    return strNumber;
  }
  
  public void setToolTip(String[][] tootltip) {
    this.tootltip = tootltip;
  }
  
  public static void drawToolTip(Graphics g, ImageFont text,  String[] tooltipText, int x, int y) {
    int maxWith = 0;
    for (int i = 0; i < tooltipText.length; i++) {
      int toolTipWidth = text.stringWidth(tooltipText[i]);
      if (toolTipWidth > maxWith) {
        maxWith = toolTipWidth;
      }
    }
    
    for (int i = 0; i < tooltipText.length; i++) {
      g.setColor(0x3CA446);
      g.fillRect(x - maxWith / 2 - 2, y - 9 + 15 * i, maxWith + 8, 20);
    }
    
    for (int i = 0; i < tooltipText.length; i++) {
      text.drawString(g, tooltipText[i], Color.WHITE_CODE, x, y - 7 + 15 * i, Constants.TOP_HCENTER_ANCHOR);
    }
  }
  
  public void pointerPressed(Sprite source, int x, int y) {
    stopTime = 0;
    pressedPoint.x = x;
    pressedPoint.y = y;
    timePressed = System.currentTimeMillis();
    
    for (int i = 0; i < items.size(); i++) {
      Sprite item = (Sprite) items.elementAt(i);
      if (item.contains(x, y)) {
        currentSelectedIndex = i;
        break;
      }
    }
    updateFocus();
  }
  
  public void pointerReleased(Sprite source, int x, int y) {
    if (items.size() == 0) {
      return;
    }
    
    stopTime = 0;
    // Process for drag
    int moveLength = 4 * (y - pressedPoint.y);
    rowMove = moveLength / verDistance;
    
    // Process for choose item
    Sprite item = (Sprite) items.elementAt(currentSelectedIndex);
    if (item.contains(x, y)) {
      if (eventListener != null) {
        if (titles != null && titles.size() > currentSelectedIndex) {
          eventListener.onActionPerform(new Event(this, String.valueOf(titles.elementAt(currentSelectedIndex))));
        } else {
          eventListener.onActionPerform(new Event(this, String.valueOf(currentSelectedIndex)));
        }
      }
      return;
    }
  }
  
  private void updateFocus() {
    if (images != null) {
      for (int i = 0; i < items.size(); i++) {
        if (images.size() > i) {
          Sprite item = (Sprite) items.elementAt(i);
          item.setImage((Image) images.elementAt(i));
        }
      }
    }
    
    if (currentSelectedIndex < 0) {
      currentSelectedIndex = 0;
    }
    
    if (brightImages != null && brightImages.size() > currentSelectedIndex) {
      Sprite item = (Sprite) items.elementAt(currentSelectedIndex);
      
      if (brightImages.elementAt(currentSelectedIndex) == images.elementAt(currentSelectedIndex)) {
        brightImages.setElementAt(ImageUtil.makeBrighter((Image) brightImages.elementAt(currentSelectedIndex), brightLevel), currentSelectedIndex);
      }
      item.setImage((Image) brightImages.elementAt(currentSelectedIndex));
    }
    checkScroll();
  }
  
  public void keyReleased(Sprite source, int keyCode) {
    stopTime = 0;
    switch (keyCode) {
    case Key.LEFT:
      int oldRow1 = (currentSelectedIndex / numberOfItemPerRow) + 1;
      int oldIndex1 = currentSelectedIndex;
      currentSelectedIndex = (currentSelectedIndex - 1 < 0) ? 0 : currentSelectedIndex - 1;
      int newRow1 = (currentSelectedIndex / numberOfItemPerRow) + 1;
      if (newRow1 != oldRow1 && isBackTabWhenBeginRow) {
        currentSelectedIndex = oldIndex1;
      }
      if (((newRow1 != oldRow1) || (oldIndex1 == 0)) && isBackTabWhenBeginRow) {
        multiTab.previousTab();
      }
      break;
    case Key.RIGHT:
      int oldRow = (currentSelectedIndex / numberOfItemPerRow) + 1;
      int oldIndex = currentSelectedIndex;
      currentSelectedIndex = (currentSelectedIndex + 1 > items.size() - 1) ? items.size() - 1 : currentSelectedIndex + 1;
      int newRow = (currentSelectedIndex / numberOfItemPerRow) + 1;
      if (newRow != oldRow && isNextTabWhenEndRow) {
        currentSelectedIndex = oldIndex;
      }
      if (((newRow != oldRow) || (oldIndex == items.size() - 1)) && isNextTabWhenEndRow) {
        multiTab.nextTab();
      }
      break;
    case Key.DOWN:
      down();
      break;
    case Key.UP:
      up();
      break;
    }
    updateFocus();
  }
  
  private void down() {
    currentSelectedIndex = (currentSelectedIndex + numberOfItemPerRow > items.size() - 1) ? currentSelectedIndex : currentSelectedIndex + numberOfItemPerRow;
  }
  
  private void up() {
    currentSelectedIndex = (currentSelectedIndex - numberOfItemPerRow < 0) ? currentSelectedIndex : currentSelectedIndex - numberOfItemPerRow;
  }
  
  private void checkScroll() {
    if (!enableScroll) {
      return;
    }
    
    int currentRow = (currentSelectedIndex / numberOfItemPerRow) + 1;
    if (dy + currentRow * verDistance > Constants.SCREEN_HEIGHT - 25) {
      dy -= verDistance;
      for (int i = 0; i < items.size(); i++) {
        Sprite item = (Sprite) items.elementAt(i);
        item.move(0, -verDistance);
      }
    } else if (dy + (currentRow - 1) * verDistance < borderY + verDistance / 2) {
      dy += verDistance;
      for (int i = 0; i < items.size(); i++) {
        Sprite item = (Sprite) items.elementAt(i);
        item.move(0, verDistance);
      }
    }
  }
  
  public void doTask() {
    stopTime += 30;
    if (rowMove > 0) {
      down();
      rowMove--;
      updateFocus();
    } else if (rowMove < 0) {
      up();
      rowMove++;
      updateFocus();
    }
  }
  
  public Sprite detroy() {
    items = null;
    images = null;
    brightImages = null;
    titles = null;
    tootltip = null;
    eventNumbers = null;
    return super.detroy();
  }
}
